package com.avocado.util;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;

/**
 * @Author: zsh
 * @Date: 2023/10/18 11:55
 */
public class ClassUtil {

    public static <T> void addClasses(String packageName, String packagePath, boolean recursive,
                                      Set<Class<T>> classes, Class<T> parentClass) throws IOException, ClassNotFoundException {
        if (!FileUtil.dirExist(packagePath)) {
            return;
        }

        List<String> files = FileUtil.listFiles(packageName, packagePath, Pattern.compile(".*.class"), recursive);
        if (files == null) {
            return;
        }
        for (String fileName : files) {
            String className = fileName.substring(0, fileName.length() - 6);
            // 添加到集合中去
//             classes.add(Class.forName(packageName + '.' +
//             className));
            // 经过回复同学的提醒，这里用forName有一些不好，会触发static方法，没有使用classLoader的load干净
            Class<?> loadClass = parentClass.getClassLoader().loadClass(className);
            if (parentClass.isAssignableFrom(loadClass) && !parentClass.equals(loadClass)) {
                @SuppressWarnings("unchecked")
                Class<T> result = (Class<T>) loadClass;
                classes.add(result);
            }
        }
    }

    public static <T> Set<Class<T>> getSubClasses(String packageName, Class<T> parentClass) {
        Set<Class<T>> classes = new LinkedHashSet<>();

        //根据包名获取目录
        String packageDir = packageName.replace('.', '/');
        Enumeration<URL> dirs;
        try {
            dirs = parentClass.getClassLoader().getResources(packageDir);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                //协议名称
                String protocol = url.getProtocol();
                if (protocol.equals("file")) {
                    String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
                    addClasses(packageName, filePath, true, classes, parentClass);
                } else if (protocol.equals("jar")) {
                    JarFile jar = ((JarURLConnection)url.openConnection()).getJarFile();
                    Enumeration<JarEntry> entries = jar.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        String name = entry.getName();
                        //如果是以/开头的
                        if (name.charAt(0) == '/') {
                            //获取后面的字符串
                            name = name.substring(1);
                        }
                        //如果前半部分和定义的包名相同
                        if (!name.startsWith(packageDir)) {
                            continue;
                        }
                        int idx = name.lastIndexOf('/');
                        //如果以"/"结尾 是一个包
                        if (idx != -1) {
                            //获取包名 把"/"替换成"."
                            packageName = name.substring(0, idx).replace('/', '.');
                        }
                        //如果是一个.class文件 而且不是目录
                        if (!name.endsWith(".class") || entry.isDirectory()) {
                            continue;
                        }
                        //去掉后面的".class" 获取真正的类名
                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                        //添加到classes
                        Class<?> loadClass = parentClass.getClassLoader().loadClass(packageName + '.' + className);
                        if (!parentClass.isAssignableFrom(loadClass) || parentClass.equals(loadClass)) {
                            continue;
                        }
                        @SuppressWarnings("unchecked")
                        Class<T> result = (Class<T>) loadClass;
                        classes.add(result);
                    }
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        return  classes;
    }

    public static <T> Set<Class<T>> getSubClasses(Class<T> parentClass) {
        return getSubClasses(parentClass.getPackage().getName(), parentClass);
    }

    /**
     * 获取当前线程的{@link ClassLoader}
     *
     * @return 当前线程的class loader
     * @see Thread#getContextClassLoader()
     */
    public static ClassLoader getContextClassLoader() {
        if (System.getSecurityManager() == null) {
            return Thread.currentThread().getContextClassLoader();
        } else {
            // 绕开权限检查
            return AccessController.doPrivileged(
                    (PrivilegedAction<ClassLoader>) () -> Thread.currentThread().getContextClassLoader());
        }
    }

    /**
     * 获取系统{@link ClassLoader}
     *
     * @return 系统{@link ClassLoader}
     * @see ClassLoader#getSystemClassLoader()
     * @since 5.7.0
     */
    public static ClassLoader getSystemClassLoader() {
        if (System.getSecurityManager() == null) {
            return ClassLoader.getSystemClassLoader();
        } else {
            // 绕开权限检查
            return AccessController.doPrivileged(
                    (PrivilegedAction<ClassLoader>) ClassLoader::getSystemClassLoader);
        }
    }

    /**
     * 获取{@link ClassLoader}<br>
     * 获取顺序如下：<br>
     *
     * <pre>
     * 1、获取当前线程的ContextClassLoader
     * 2、获取当前类对应的ClassLoader
     * 3、获取系统ClassLoader（{@link ClassLoader#getSystemClassLoader()}）
     * </pre>
     *
     * @return 类加载器
     */
    public static ClassLoader getClassLoader() {
        ClassLoader classLoader = getContextClassLoader();
        if (classLoader == null) {
            classLoader = ClassUtil.class.getClassLoader();
            if (null == classLoader) {
                classLoader = getSystemClassLoader();
            }
        }
        return classLoader;
    }

    public static List<Class> getClasses(String prefix) throws Exception {
        List<Class> list = new ArrayList<>();

        String scan = prefix.replaceAll("\\.", "/");
        URL url = getClassLoader().getResource(scan);
        if (url.getProtocol().equals("file")) {
            List<File> files = new ArrayList<>();
            listFiles(new File(url.getFile()), files);
            list.addAll(loadeClasses(files, scan));
        }

        return list;
    }

    private static List<Class<?>> loadeClasses(List<File> classes,String scan) throws ClassNotFoundException {
        List<Class<?>> clazzes = new ArrayList<Class<?>>();
        for(File file : classes) {
            String fPath = file.getAbsolutePath().replaceAll("\\\\","/") ;
            // 把 包路径 前面的 盘符等 去掉 ， 这里必须是lastIndexOf ，防止名称有重复的
            String packageName = fPath.substring(fPath.lastIndexOf(scan));
            packageName = packageName.replace(".class","").replaceAll("/", ".");
            // 根据名称加载类
            clazzes.add(Class.forName(packageName));
        }
        return clazzes;
    }

    /** * 查找所有的文件 * * @param dir 路径 * @param fileList 文件集合 */
    private static void listFiles(File dir, List<File> fileList) {
        if (dir.isDirectory()) {
            for (File f : dir.listFiles()) {
                listFiles(f, fileList);
            }
        } else {
            if(dir.getName().endsWith(".class")) {
                fileList.add(dir);
            }
        }
    }
}
